package net.yihang.miaosha.service;

import com.alibaba.fastjson.JSON;
import net.yihang.miaosha.controller.result.CodeMsg;
import net.yihang.miaosha.domain.GoodsKey;
import net.yihang.miaosha.redis.Key;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import redis.clients.jedis.Jedis;
import redis.clients.jedis.JedisPool;
import redis.clients.jedis.Transaction;

import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.UUID;

import static java.lang.Integer.parseInt;

@Service
public class RedisService {

    @Autowired
    private JedisPool pool;
//    @Autowired
//    private JedisExecutor executor;

    public <T> T get(Key key, Class<T> clazz) {
        try (Jedis jedis = pool.getResource()) {
            String json = jedis.get(key.key());
            return JSON.parseObject(json, clazz);
        }
        /*return executor.exeucte((jedis) -> {
            String json = jedis.get(key.key());
            return JSON.parseObject(json, clazz);
        }, clazz);*/
    }

    public void hset(Key key, String field, String value){
        try (Jedis jedis = pool.getResource()) {
            jedis.hset(key.key(), field, value);
        }
    }

    public long lpush(Key key, String... value) {
        try (Jedis jedis = pool.getResource()) {
            return jedis.lpush(key.key(), value);
        }
    }

    public String rpop(Key key) {
        try (Jedis jedis = pool.getResource()) {
            return jedis.rpop(key.key());
        }
    }

    public void del(Key key) {
        try (Jedis jedis = pool.getResource()) {
            jedis.del(key.key());
        }
    }

    public void set(Key key, Object value) {
        try (Jedis jedis = pool.getResource()) {
            String json = JSON.toJSONString(value);
            if (key.expireTime() <= 0) {
                jedis.set(key.key(), json);
            } else {
                jedis.setex(key.key(), key.expireTime(), json);
            }
        }
        /*executor.exeucte(jedis -> {
            String json = JSON.toJSONString(value);
            if (key.expireTime() <= 0) {
                jedis.set(key.key(), json);
            } else {
                jedis.setex(key.key(), key.expireTime(), json);
            }
            return null;
        });*/
    }
    public CodeMsg miaosha(long goodsId) {
        try (Jedis jedis = pool.getResource()) {
            // 库存的key
            GoodsKey stockKey = GoodsKey.stockKey(goodsId);
            String key = stockKey.key();
            while(true) {
                // 盯住这个key
                jedis.watch(key);

                // 拿到库存
                int stock = parseInt(jedis.get(key));
                if (stock <=0) {
                    jedis.unwatch();
                    return CodeMsg.STOCK_EMPTY;
                }
                Transaction multi = jedis.multi();
                multi.decr(key); // 库存-1
                List<Object> result = multi.exec(); // 把decr 命令发给服务器
                jedis.unwatch();
                if(result != null&&!result.isEmpty()) {
//                    System.out.println(key +":"+stock+"========>" +result);
                    break;
                }

            }
            return CodeMsg.MIAOSHA_SUCCESS;
        }
    }


    // 分布式锁（悲观锁）
    /*public CodeMsg miaosha(long goodsId) {
        try (Jedis jedis = pool.getResource()) {

            // 库存 key    商品1  5
            GoodsKey stockKey = GoodsKey.stockKey(goodsId);
            //  解锁用的value
            String value = UUID.randomUUID().toString();
            //  锁的key
            Key lockKey = GoodsKey.lockKey(goodsId);
            while(true) {
                // 查询库存 5
                int stock = parseInt(jedis.get(stockKey.key()));
                if (stock <= 0) {
                    return CodeMsg.STOCK_EMPTY;
                }

                try {
                    if (! trylock(jedis, lockKey, value)) {
                        continue;
                    }
                    // 加锁成功
                    stock = parseInt(jedis.get(stockKey.key()));
                    if(stock <= 0) {
                        return CodeMsg.STOCK_EMPTY;
                    }
                    // 如果有库存
                    Long decr = jedis.decr(stockKey.key()); // 库存-1
                    return CodeMsg.MIAOSHA_SUCCESS;
                } finally {
                    unlock(jedis, lockKey, value);
                }
            }
        }
    }*/


    private static final String LOCK_SUCCESS = "OK";
    private static final Long UNLOCK_SUCCESS = 1L;
    private static final String script = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
    private boolean unlock(Jedis jedis, Key key, String value) {
        Object result = jedis.eval(script, Collections.singletonList(key.key()), Collections.singletonList(value));
        return UNLOCK_SUCCESS.equals(result);
    }
    private boolean trylock(Jedis jedis, Key key, String value){
        String result = jedis.set(key.key(), value, "NX", "PX", 10000);
        return LOCK_SUCCESS.equals(result);
    }
}
